package jfilters.filters.render;

/*
    Copyright Janne Liljeblad 2008.

    This file is part of JFilters.

    JFilters is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    JFilters is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with JFilters.  If not, see <http://www.gnu.org/licenses/>.
*/

import jfilters.libmath.JFVector2;
import jfilters.filters.SingleInputFilter;
import jfilters.lib.JavaGLib;
import jfilters.libmath.JFMath;
import java.util.Random;
import java.awt.image.*;
import java.awt.*;

//--- Draws a multicolored plasma pattern image.
//--- gimp file: /plug-ins/common/plasma.c
public class JFPlasma extends SingleInputFilter
{
	private int  seed = 0;
	private double   turbulence = 1.0;
	private boolean  random_seed = false;

	private int       ix1, iy1, ix2, iy2;
	private int       bpp, alpha;
	private boolean   has_alpha;

	private Random random;

	public JFPlasma(){}

	//--- turbulence of pattern, range 0.1 - 7.0S
	public void setTurbulence( double val )
	{
		if( val < 0.1 ) val = 0.1;
		if( val > 7.0 ) val = 7.0;
		turbulence = val;
	}

	public BufferedImage filter( BufferedImage destinationImg )
	{
		int depth;

		WritableRaster destination = destinationImg.getRaster();

		int ix1 = 0;
		int iy1 = 0;
		int ix2 = destination.getWidth();
		int iy2 = destination.getHeight();
	
		bpp = destination.getNumBands();
  		has_alpha = ( bpp == 4 );
  		alpha = (has_alpha) ? bpp - 1 : bpp;
		
		random = new Random();

		if (ix1 != ix2 && iy1 != iy2)
		{
			/*
			* This first time only puts in the seed pixels - one in each
			* corner, and one in the center of each edge, plus one in the
			* center of the image.
			*/
			
			doPlasma ( destination, ix1, iy1, ix2 - 1, iy2 - 1, -1, 0 );
		
			/*
			* Now we recurse through the images, going further each time.
			*/
			depth = 1;
			while ( !doPlasma (destination, ix1, iy1, ix2 - 1, iy2 - 1, depth, 0 ) )
			{
				depth++;
			}
		}
	
		return destinationImg;
	}

	private boolean doPlasma (	WritableRaster   pft,
					int              x1,
					int              y1,
					int              x2,
					int              y2,
					int              depth,
					int              scale_depth )
		
	{
		int[] tl = new int[4];
		int[] ml = new int[4];
		int[] bl = new int[4];
		int[] mt = new int[4];
		int[] mm = new int[4];
		int[] mb = new int[4];
		int[] tr = new int[4];
		int[] mr = new int[4];
		int[] br = new int[4];
		int[] tmp = new int[4];
		int xm, ym;
		
		int count = 0;
		
		xm = (x1 + x2) / 2;
		ym = (y1 + y2) / 2;
		
		/* Initial pass through - no averaging. */
		if (depth == -1)
		{
			randomRgb( tl);
			pft.setPixel( x1, y1, tl);
			randomRgb( tr);
			pft.setPixel(  x2, y1, tr);
			randomRgb( bl);
			pft.setPixel( x1, y2, bl);
			randomRgb( br);
			pft.setPixel( x2, y2, br);
			randomRgb( mm);
			pft.setPixel(  xm, ym, mm);
			randomRgb( ml);
			pft.setPixel( x1, ym, ml);
			randomRgb( mr);
			pft.setPixel( x2, ym, mr);
			randomRgb( mt);
			pft.setPixel( xm, y1, mt);
			randomRgb( mb);
			pft.setPixel( xm, y2, mb);
			
			return false;
		}
		
		/*
		* Some later pass, at the bottom of this pass,
		* with averaging at this depth.
		*/
		if (depth == 0)
		{
			int ran;
			
			if (x1 == x2 && y1 == y2) return false;
			
			pft.getPixel( x1, y1, tl);
			pft.getPixel( x1, y2, bl);
			pft.getPixel( x2, y1, tr);
			pft.getPixel( x2, y2, br);
			
			ran = (int) ((256.0 / (2.0 * scale_depth)) * turbulence);
			
			if (xm != x1 || xm != x2)
			{
				/* Left. */
				averagePixel( ml, tl, bl, bpp);
				addRandom( ml, ran);
				pft.setPixel( x1, ym, ml);
			
				if (x1 != x2)
				{
					/* Right. */
					averagePixel( mr, tr, br, bpp);
					addRandom( mr, ran);
					pft.setPixel( x2, ym, mr);
				}
			}
			
			if (ym != y1 || ym != y2)
			{
				if (x1 != xm || ym != y2)
				{
					/* Bottom. */
					averagePixel( mb, bl, br, bpp);
					addRandom( mb, ran);
					pft.setPixel( xm, y2, mb);
				}
			
				if (y1 != y2)
				{
					/* Top. */
					averagePixel( mt, tl, tr, bpp);
					addRandom( mt, ran);
					pft.setPixel( xm, y1, mt);
				}
			}
			
			if (y1 != y2 || x1 != x2)
			{
				/* Middle pixel. */
				averagePixel( mm, tl, br, bpp);
				averagePixel( tmp, bl, tr, bpp);
				averagePixel( mm, mm, tmp, bpp);
			
				addRandom( mm, ran);
				pft.setPixel( xm, ym, mm);
			}
			
			count++;
	
			return ((x2 - x1) < 3) && ((y2 - y1) < 3);
		}
		
		if (x1 < x2 || y1 < y2)
		{
			/* Top left. */
			doPlasma (pft, x1, y1, xm, ym, depth - 1, scale_depth + 1 );
			/* Bottom left. */
			doPlasma (pft, x1, ym, xm ,y2, depth - 1, scale_depth + 1 );
			/* Top right. */
			doPlasma (pft, xm, y1, x2 , ym, depth - 1, scale_depth + 1 );
			/* Bottom right. */
			return doPlasma (pft, xm, ym, x2, y2, depth - 1, scale_depth + 1 );
		}
		else
		{
			return true;
		}
		
	}

	public void averagePixel ( int[] dest, int[] src1, int[] src2, int bpp1 )
	{
		for ( int i = 0; i < bpp1; i++ )
			dest[i] = ( src1[ i ] + src2[i] ) / 2;
	}

	public void randomRgb( int[] pixel )
	{
		for (int i = 0; i < alpha; i++) pixel[i] = JavaGLib.g_rand_int_range(  0, 256, random );
		
		if (has_alpha) pixel[alpha] = 255;
	}

	public  void addRandom ( int[] pixel, int amount )
	{
		amount /= 2;
		
		if (amount > 0)
		{
			int i, tmp;
		
			for (i = 0; i < alpha; i++)
			{
				tmp = pixel[i] + JavaGLib.g_rand_int_range( -amount, amount, random );
				pixel[i] = JFMath.CLAMP( tmp, 0, 255);
			}
		}
	}

}//end class